Problématique: Comment pourrait-on reconnaitre le modèle d'un avion sans être forcément en contact avec le cockpit.¶

A 380

Étant passionné d'aviation, je m'efforce toujours d'identifier les avions que j'aperçois. Certains sont relativement faciles à reconnaître grâce à leurs formes originales et spécifiques. Par exemple, les ailes de l'Airbus A380, avec leur forme ovale et leur taille imposante, sont uniques à cet appareil. Pour anecdote, les ailes de l'A380 s'inspirent de celles des rapaces, comme le faucon, l'animal aérien le plus rapide au monde et l'un des plus imposants. Malgré sa taille, il parvient à se stabiliser parfaitement en ajustant l'orientation des plumes situées à l'extrémité de ses ailes. Les concepteurs de l'A380 se sont inspirés de ce mécanisme, ce qui explique pourquoi l'extrémité des ailes de l'A380 ressemble à un petit triangle.

Cependant, pour la majorité des avions, les différences se situent dans de petits détails de la structure de l'appareil. Par exemple, comment distinguer un Airbus A320 d'un Boeing 737? En observant attentivement le nez de l'appareil, on remarque que celui du 737 est légèrement plus géométrique. S'appuyer sur la forme des ailes serait complexe, car les deux modèles possèdent des ailes plates.

Les différences peuvent également résider dans des points plus subtils, comme la taille des réacteurs, ce qui est plus difficile à distinguer à l'œil nu.

Au-delà de la passion, l'identification précise des avions peut s'avérer cruciale. Imaginez une tour de contrôle perdant le contact avec des avions à proximité, et un avion souhaitant atterrir : comment savoir si l'aéroport peut l'accueillir? Un système de détection d'avions, capable d'identifier le modèle de l'appareil et d'informer le pilote via un panneau électrique de la possibilité d'atterrissage, pourrait également alerter les services de secours pour qu'ils se préparent à une intervention d'urgence en cas de risque d'accident.

Ceci n'est qu'un exemple, mais il illustre bien l'utilité et la praticité d'un tel système.

L'enjeu est donc de savoir comment entraîner une intelligence artificielle à reconnaître toutes les subtilités d'un modèle d'avion, afin de pouvoir identifier avec une certaine certitude de quel modèle il s'agit.

Imports¶

In [ ]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import os
In [ ]:
from tensorflow.keras.preprocessing.image import ImageDataGenerator
2024-01-24 15:00:12.552093: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
In [ ]:
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import EarlyStopping, LearningRateScheduler
In [ ]:
import seaborn as sns
In [ ]:
from tensorflow.keras.preprocessing import image
In [ ]:
from PIL import Image

Construction des d'entrainement et de validation¶

In [ ]:
# Repertoire de base
base_dir = "/Users/aliou/Downloads/huge_projects/aircraft_reco/final_data"
In [ ]:
train_dir = base_dir + '/train'
test_dir = base_dir + '/test'

# Repertoires des images d'entrainement et de test
a_320_train_dir = train_dir + '/Airbus_320'
a_320_test_dir = test_dir + '/Airbus_320'

a_340_train_dir = train_dir + '/Airbus_340'
a_340_test_dir = test_dir + '/Airbus_340'

a_380_train_dir = train_dir + '/Airbus_380'
a_380_test_dir = test_dir + '/Airbus_380'

b_737_train_dir = train_dir + '/Boeing_737'
b_737_test_dir = test_dir + '/Boeing_737'

b_747_train_dir = train_dir + '/Boeing_747'
b_747_test_dir = test_dir + '/Boeing_747'

b_767_train_dir = train_dir + '/Boeing_767'
b_767_test_dir = test_dir + '/Boeing_767'

embraer_ejet_train_dir = train_dir + '/Embraer_ejet'
embraer_ejet_test_dir = test_dir + '/Embraer_ejet'
In [ ]:
# Nombre total d'images contenu dans chaque repertoire
print('total training A320 images :', len(os.listdir(      a_320_train_dir ) ))
print('total training A340 images :', len(os.listdir(      a_340_train_dir ) ))
print('total training A380 images :', len(os.listdir(      a_380_train_dir ) ))
print('total training B737 images :', len(os.listdir(      b_737_train_dir ) ))
print('total training B747 images :', len(os.listdir(      b_747_train_dir ) ))
print('total training B767 images :', len(os.listdir(      b_767_train_dir ) ))
print('total training EEJET images :', len(os.listdir(      embraer_ejet_train_dir ) ))
print()
print('total test A320 images :', len(os.listdir(      a_320_test_dir ) ))
print('total test A340 images :', len(os.listdir(      a_340_test_dir ) ))
print('total test A380 images :', len(os.listdir(      a_380_test_dir ) ))
print('total test B737 images :', len(os.listdir(      b_737_test_dir ) ))
print('total test B747 images :', len(os.listdir(      b_747_test_dir ) ))
print('total test B767 images :', len(os.listdir(      b_767_test_dir ) ))
print('total test EEJET images :', len(os.listdir(      embraer_ejet_test_dir ) ))
total training A320 images : 101
total training A340 images : 101
total training A380 images : 101
total training B737 images : 101
total training B747 images : 101
total training B767 images : 101
total training EEJET images : 91

total test A320 images : 11
total test A340 images : 11
total test A380 images : 11
total test B737 images : 11
total test B747 images : 11
total test B767 images : 11
total test EEJET images : 11

Visualisation de quelques échantillons¶

In [ ]:
# Fonction de visualisation
def plot_samples(dir, sample, n_ligne = 3, n_col = 4):

    """
        Cette fonction permet de visualiser un échantillons d'image dans une grille NxM (Par défaut: 3 x 4).

        Paramètre:
            dir: Repertoire où se situe les images,
            sample: Echantillons d'image,
            n_ligne: Nombre de lignes,
            n_col: Nombre de colonnes
    """

    plt.figure(figsize = ((n_ligne * n_col), (max(n_ligne, n_col) * 2) ))

    for i, aircraft in enumerate(sample): # Itérer sur l'échantillons donné

        if aircraft != '.DS_Store': # Ignorer le fichier caché .DS_Store

            # Définir une grille de visualisation
            ax = plt.subplot(n_ligne, n_col, i + 1)

            # Combiner le nom du fichier actuel avec le repertoire donné et plotter.
            img = mpimg.imread(os.path.join(dir, aircraft))
            plt.axis("off")
            plt.imshow(img)
            
            # Mettre le nom du fichier comme titre
            ax.set_title(aircraft)

A 320

In [ ]:
# Choisir 12 échantillons au hasard puis faire appel à la fonction de plot
a_320_sample = np.random.choice(os.listdir(a_320_train_dir),size = 12)
plot_samples(a_320_train_dir, a_320_sample)

A 340

In [ ]:
a_340_sample = np.random.choice(os.listdir(a_340_train_dir),size = 12)
plot_samples(a_340_train_dir, a_340_sample)

A 380

In [ ]:
a_380_sample = np.random.choice(os.listdir(a_380_train_dir),size = 12)
plot_samples(a_380_train_dir, a_380_sample)

B 737

In [ ]:
b_737_sample = np.random.choice(os.listdir(b_737_train_dir),size = 12)
plot_samples(b_737_train_dir, b_737_sample)

B 747

In [ ]:
b_747_sample = np.random.choice(os.listdir(b_747_train_dir),size = 12)
plot_samples(b_747_train_dir, b_747_sample)

B 767

In [ ]:
b_767_sample = np.random.choice(os.listdir(b_767_train_dir),size = 12)
plot_samples(b_767_train_dir, b_767_sample)

Embraer Ejet

In [ ]:
e_ejet_sample = np.random.choice(os.listdir(embraer_ejet_train_dir),size = 12)
plot_samples(embraer_ejet_train_dir, e_ejet_sample)

Augmentation de données¶

Paramètre de l'objet ImageDataGenerator:

  • rescale = 1/255.0: Normaliser les images de 0 à 255.
  • rotation_range = 10: Changer l'orientation de l'image avec un dégré de 10 (Ce choix est le dernier retenu après le dernier modèle), conte tenu du fait les images d'avions sont généralement peu sujet au inclinaison, car les points de vues sont généralement les même, une petite rotation assure une modification pas trop excéssive pour la phase de test.
  • fill_mode = 'nearest': Définit comment remplir les pixels vides des images générer. Défault, nearest: copie les pixels voisin pour remplir les pixels vides.

Paramètre des générateur d'image:

  • train_dir: Repertoire d'entrainement,
  • target_size = (500, 500): Découper les images à cette dimension 500x500 pixels, pour le Convolutional Neural Network(cnn),
  • batch_size = 10: Nombre d'image par batch.
  • class_mode = 'sparse': 'sparse', car il existe plusieurs classes dans le jeu de données
  • color_mode = 'grayscale': Mettre les images en gris pour que le modèle apperçoive mieux les features
In [ ]:
target_dim = (500, 500)

# Créer un object de la classe ImageDataGenerator
train_datagen = ImageDataGenerator(
    rescale = 1/255.0,
    rotation_range = 10,
    fill_mode = 'nearest' 
)

test_datagen = ImageDataGenerator(rescale = 1/255.0)

# Charger et transformer les images des repertoires d'entrainement
train_generator = train_datagen.flow_from_directory(
    train_dir,
    target_size = target_dim,
    batch_size = 10,
    class_mode = 'sparse',
    color_mode = 'grayscale'
)

test_generator = test_datagen.flow_from_directory(
    test_dir,
    target_size = target_dim,
    batch_size = 10,
    class_mode = 'sparse',
    color_mode = 'grayscale'
)
Found 690 images belonging to 7 classes.
Found 70 images belonging to 7 classes.
In [ ]:
# Comment est-ce que le générateur à ordonné les classes
test_generator.class_indices
Out[ ]:
{'Airbus_320': 0,
 'Airbus_340': 1,
 'Airbus_380': 2,
 'Boeing_737': 3,
 'Boeing_747': 4,
 'Boeing_767': 5,
 'Embraer_ejet': 6}

Construction du modèle séquentiel¶

input_shape = (500, 500, 1) : J'ai utilisé plusieurs input shape au final 500x500 marche mieux pour l'entrainement, car les images du dataset sont de qualité relativement bonne entre 600x? à du HD. Le paramètre 1, spécifie le nombre de channel qu'attendra le modèle 1: pour des images de gris et 3: pour des rgb.

CNN: J'ai opté pour un cnn car le meilleur choix.

  • Nombre de filtre: Le nombre de filtre est très important car un nombre de filtre beaucoup trop haut au empêche de le modèle de capter certains spécifité dans les images, A contrario un nombre de filtre trop faible capte énormement les bruits du modèle. C'est donc un paramètre sur lequel il faut expérimenter pour l'amélioration du modèle.
  • La fonctino d'activation 'ReLU' permet de prendre le maximum parmis les inputs reçu par les neuronnes, vu que j'essaye de calculer la probablité qu'un avions appartienne à une des 7 classes, cette fonction d'activation est un très bon choix.
  • BatchNormalization: Cette fonction permet d'améliorer le processus d'entrainement en normalisant les données au niveau des couches du réseau.
  • Flatten(): Cette fonction transforme les matrices n dimension à une matrice à 1 dimension
  • Dense(): Créer une couche caché paramètre: (Nombre de neuronnes, fonction d'activation). La dernière couche Dense() est la couche de sortie, paramètre: (Nombre de classes, fonction d'activation). Ici la fonction d'activation que j'ai retenu c'est le softmax: qui est une fonction exponentielle normalisée, qui va convertir le vecteur d'entré du neuronne en une distribution de probabilité.
In [ ]:
input_shape = (500, 500, 1) 
num_classes = 7  # Classes identifier

model = Sequential([

    Conv2D(4, (3, 3), input_shape=input_shape, activation='relu'),
    MaxPooling2D((2, 2)),
    BatchNormalization(),

    Conv2D(8, (3, 3), activation='relu'),
    MaxPooling2D((2, 2)),
    BatchNormalization(),

    Conv2D(16, (3, 3), activation='relu'),
    MaxPooling2D((2, 2)),
    BatchNormalization(),
    
    Flatten(),
    Dropout(0.4),

    Dense(32, activation='relu'),
    BatchNormalization(),
    
    Dense(num_classes, activation='softmax')  # Use softmax for multi-class classification
])
In [ ]:
model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 498, 498, 4)       40        
                                                                 
 max_pooling2d (MaxPooling2  (None, 249, 249, 4)       0         
 D)                                                              
                                                                 
 batch_normalization (Batch  (None, 249, 249, 4)       16        
 Normalization)                                                  
                                                                 
 conv2d_1 (Conv2D)           (None, 247, 247, 8)       296       
                                                                 
 max_pooling2d_1 (MaxPoolin  (None, 123, 123, 8)       0         
 g2D)                                                            
                                                                 
 batch_normalization_1 (Bat  (None, 123, 123, 8)       32        
 chNormalization)                                                
                                                                 
 conv2d_2 (Conv2D)           (None, 121, 121, 16)      1168      
                                                                 
 max_pooling2d_2 (MaxPoolin  (None, 60, 60, 16)        0         
 g2D)                                                            
                                                                 
 batch_normalization_2 (Bat  (None, 60, 60, 16)        64        
 chNormalization)                                                
                                                                 
 flatten (Flatten)           (None, 57600)             0         
                                                                 
 dropout (Dropout)           (None, 57600)             0         
                                                                 
 dense (Dense)               (None, 32)                1843232   
                                                                 
 batch_normalization_3 (Bat  (None, 32)                128       
 chNormalization)                                                
                                                                 
 dense_1 (Dense)             (None, 7)                 231       
                                                                 
=================================================================
Total params: 1845207 (7.04 MB)
Trainable params: 1845087 (7.04 MB)
Non-trainable params: 120 (480.00 Byte)
_________________________________________________________________

Compile the model¶

  • Optimiseur (Adam): Basé sur l'optimisation stochastic, cet optimiseur est très performant, pas très gourmant en terme de mémoire, fonctionne très bien avec les problème qui demandent beaucoup de données.
  • sparse_categorical_crossentropy: J'utilise cette fonction car elle permet de trouver un index de la classe la plus probable prédite.
  • Comme metrics j'ai pris l'(accuracy) pour avoir les mesures de performance.
In [ ]:
# Compile the model
model.compile(optimizer = Adam(),
              loss = 'sparse_categorical_crossentropy',
              metrics = ['accuracy'])

Early stopping callback()¶

Créer un objet de la classe EarlyStopping pour surveiller la loss avec un niveau de patience de 5 steps:

  • Si au bout de 5 pas la valeur de la loss fluctue trop, on arrête l'entraînement.
In [ ]:
early_stopping = EarlyStopping(monitor = 'val_loss', patience = 5, restore_best_weights = True)

Learning rate scheduler callback()¶

Optimiser le taux d'apprentissage au délà de 10 epochs pour améliorer l'apprentissage

In [ ]:
def learning_rate_scheduler(epochs):

    if epochs < 10:
        return 0.001
    else:
        return 0.001 * tf.math.exp(0.1 * (10 - epochs))
In [ ]:
# Créer un objet de la classe 'LearningRateScheduler' et envoyer la fonction de programmation du taux d'entrainement.
learning_rate_schedule = LearningRateScheduler(learning_rate_scheduler)

Fit the model¶

Entrainement du modèle:

  • Rappel: A ce stade les données font partir des données augmenter avec ImageDataGenerator donc on donne la variable train_generator à la fonction fit() pour l'entrainement et test_generator pour la validation.
  • Le nombre de pas par epoch: défini par la taille du train_generator.
  • epochs: Cette variable détermine le nombre de cycle d'entrainement à réaliser.
  • callbacks: Appel des fonctions de contrôle réalisées plus haut.
In [ ]:
history = model.fit(
    train_generator,
    steps_per_epoch = len(train_generator),
    epochs = 30,
    validation_data = test_generator,
    validation_steps = len(test_generator),
    callbacks = [early_stopping, learning_rate_schedule]
)
Epoch 1/30
69/69 [==============================] - 68s 950ms/step - loss: 2.0779 - accuracy: 0.2188 - val_loss: 2.1155 - val_accuracy: 0.1429 - lr: 0.0010
Epoch 2/30
69/69 [==============================] - 65s 939ms/step - loss: 1.6853 - accuracy: 0.3725 - val_loss: 2.5495 - val_accuracy: 0.1571 - lr: 0.0010
Epoch 3/30
69/69 [==============================] - 65s 941ms/step - loss: 1.4748 - accuracy: 0.4696 - val_loss: 2.5210 - val_accuracy: 0.1714 - lr: 0.0010
Epoch 4/30
69/69 [==============================] - 65s 942ms/step - loss: 1.3542 - accuracy: 0.5116 - val_loss: 2.3159 - val_accuracy: 0.1857 - lr: 0.0010
Epoch 5/30
69/69 [==============================] - 65s 939ms/step - loss: 1.1072 - accuracy: 0.6101 - val_loss: 1.9374 - val_accuracy: 0.2714 - lr: 0.0010
Epoch 6/30
69/69 [==============================] - 67s 974ms/step - loss: 0.9232 - accuracy: 0.7014 - val_loss: 1.9540 - val_accuracy: 0.2714 - lr: 0.0010
Epoch 7/30
69/69 [==============================] - 65s 927ms/step - loss: 0.8174 - accuracy: 0.7609 - val_loss: 1.4949 - val_accuracy: 0.5143 - lr: 0.0010
Epoch 8/30
69/69 [==============================] - 66s 957ms/step - loss: 0.6936 - accuracy: 0.7783 - val_loss: 1.6028 - val_accuracy: 0.4571 - lr: 0.0010
Epoch 9/30
69/69 [==============================] - 67s 965ms/step - loss: 0.6030 - accuracy: 0.8420 - val_loss: 1.6819 - val_accuracy: 0.4714 - lr: 0.0010
Epoch 10/30
69/69 [==============================] - 65s 940ms/step - loss: 0.6431 - accuracy: 0.7884 - val_loss: 1.3184 - val_accuracy: 0.6429 - lr: 0.0010
Epoch 11/30
69/69 [==============================] - 59s 856ms/step - loss: 0.5031 - accuracy: 0.8580 - val_loss: 1.3308 - val_accuracy: 0.6000 - lr: 0.0010
Epoch 12/30
69/69 [==============================] - 59s 842ms/step - loss: 0.4055 - accuracy: 0.8754 - val_loss: 1.7477 - val_accuracy: 0.5143 - lr: 9.0484e-04
Epoch 13/30
69/69 [==============================] - 59s 849ms/step - loss: 0.3841 - accuracy: 0.8870 - val_loss: 1.3965 - val_accuracy: 0.6429 - lr: 8.1873e-04
Epoch 14/30
69/69 [==============================] - 59s 851ms/step - loss: 0.3751 - accuracy: 0.9000 - val_loss: 1.4221 - val_accuracy: 0.6143 - lr: 7.4082e-04
Epoch 15/30
69/69 [==============================] - 60s 852ms/step - loss: 0.3432 - accuracy: 0.9058 - val_loss: 1.2717 - val_accuracy: 0.6571 - lr: 6.7032e-04
Epoch 16/30
69/69 [==============================] - 60s 861ms/step - loss: 0.3333 - accuracy: 0.8971 - val_loss: 1.4467 - val_accuracy: 0.6429 - lr: 6.0653e-04
Epoch 17/30
69/69 [==============================] - 60s 873ms/step - loss: 0.2902 - accuracy: 0.9261 - val_loss: 1.5307 - val_accuracy: 0.6000 - lr: 5.4881e-04
Epoch 18/30
69/69 [==============================] - 62s 877ms/step - loss: 0.2400 - accuracy: 0.9333 - val_loss: 1.3586 - val_accuracy: 0.6429 - lr: 4.9659e-04
Epoch 19/30
69/69 [==============================] - 61s 860ms/step - loss: 0.2231 - accuracy: 0.9478 - val_loss: 1.4792 - val_accuracy: 0.6143 - lr: 4.4933e-04
Epoch 20/30
69/69 [==============================] - 60s 870ms/step - loss: 0.1809 - accuracy: 0.9609 - val_loss: 1.4355 - val_accuracy: 0.6000 - lr: 4.0657e-04
In [ ]:
# Les variables de mesure de performances
history.history.keys()
Out[ ]:
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy', 'lr'])
In [ ]:
acc = history.history['accuracy'][-1]

loss = history.history['loss'][-1]

print(f"accuracy: {acc}\nloss: {loss}")
accuracy: 0.960869550704956
loss: 0.18091294169425964

Check for overfitting¶

In [ ]:
sns.set()
plt.figure(figsize = (12,6))
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'Validation'], loc='upper left')
plt.show()
In [ ]:
sns.set()
plt.figure(figsize = (12,6))
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'Validation loss'], loc='upper left')
plt.show()

Conclusions:¶

A partir des courbes de train/validation et loss/validation, on constate un certains écart et une certaine variances entre les courbes, on peut en conclure que l'apprentissage n'est peut être optimiser avec plus de données et une optimisation des hyperparamètres on devrait pour obtenir une meilleur courbe d'apprentissage.

Prédiction¶

In [ ]:
# Créer un dictionnaire des classes pour déterminer la classe prédite de manière dynamique.
classes = {
    0 : 'Airbus A 320',
    1 : 'Airbus A 340',
    2 : 'Airbus A 380',
    3 : 'Boeing 737',
    4 : 'Boeing 747',
    5 : 'Boeing 767',
    6 : 'Embraer E jet'
}
In [ ]:
def rgb_to_grayscale(path):

    """
        Transforme l'image à prédire, pour quelle puisse être accepté par le modèle.

        Paramètre:
            path: Chemin d'accès de l'image.
    """
    rgb_image = Image.open(path) # Ouvrir l'image

    grayscale_image = rgb_image.convert('L') # Transformer l'image en gris.
    grayscale_image = np.array(grayscale_image) # Convertir l'image en une matrice de numpy

    # Rédimentionner à l'(input shape) souhaité
    np_image = np.array(Image.fromarray(grayscale_image).resize((500, 500)))

    # Normaliser l'image
    np_image_nomalized = np_image / 255.0
    
    return np_image_nomalized
In [ ]:
def predictions_test_set(path, cl : int):

    """
        Cette fonction permet de predir chaque élément contenu dans un repertoire donnée.

        Paramètre:
            path: Chemin d'accès du repertoire.
            cl: Classe des images du repertoire. (Car je l'ai grouper par classe)
    """

    files = os.listdir(path) # Obtenir une liste des noms des images du répertoire
    n_true = 0 # Variable de comptage des vraies prédictions

    for _, filename in enumerate(files):
        
        if filename != '.DS_Store': # Ignorer le fichier caché '.DS_Store'
            
            img = rgb_to_grayscale(path + '/' + filename) # Construire le chémin de l'image et la transformer
            preds_probs = model.predict(np.expand_dims(img, axis = 0)) # Obtenir la liste des prédictions
            predicted = np.argmax(preds_probs, axis = 1)[0] # Sélectionner le maximum et accéder à  l'indice

            # Afficher les prédictions en fonction de la véracité de la prédiction
            if cl == predicted:
                n_true += 1
                print(f"True: Image: {filename}, predicted: {classes[predicted]}")
            else:
                print(f"False: Image: {filename}, predicted: {classes[predicted]}")

    print('************')
    print(f"True predictions: {n_true} / {len(files) - 1}") # Afficher la rapport des vraies prédictions
In [ ]:
predictions_test_set(a_380_test_dir, 2)
1/1 [==============================] - 0s 55ms/step
True: Image: a380_9872518.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 49ms/step
True: Image: a380_9716284.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 62ms/step
True: Image: a380_1979068.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 46ms/step
True: Image: a380_9861562.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 54ms/step
True: Image: a380_9826128.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 47ms/step
True: Image: a380_9817263.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 51ms/step
True: Image: a380_9832618.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 46ms/step
True: Image: a380_9732825.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 50ms/step
True: Image: a380_9871652.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 48ms/step
True: Image: a380_9865134.jpg, predicted: Airbus A 380
************
True predictions: 10 / 10
In [ ]:
predictions_test_set(a_320_test_dir, 0)
1/1 [==============================] - 0s 49ms/step
True: Image: a320_2194912.jpg, predicted: Airbus A 320
1/1 [==============================] - 0s 53ms/step
True: Image: a320_1838645.jpg, predicted: Airbus A 320
1/1 [==============================] - 0s 54ms/step
True: Image: a320_1882508.jpg, predicted: Airbus A 320
1/1 [==============================] - 0s 49ms/step
False: Image: a320_1921311.jpg, predicted: Embraer E jet
1/1 [==============================] - 0s 53ms/step
True: Image: a320_2164953.jpg, predicted: Airbus A 320
1/1 [==============================] - 0s 66ms/step
True: Image: a320_1878567.jpg, predicted: Airbus A 320
1/1 [==============================] - 0s 70ms/step
False: Image: a320_1876402.jpg, predicted: Boeing 747
1/1 [==============================] - 0s 48ms/step
False: Image: a320_1871273.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 51ms/step
True: Image: a320_1813838.jpg, predicted: Airbus A 320
1/1 [==============================] - 0s 49ms/step
False: Image: a320_2069707.jpg, predicted: Boeing 767
************
True predictions: 6 / 10
In [ ]:
predictions_test_set(a_340_test_dir, 1)
1/1 [==============================] - 0s 51ms/step
False: Image: a340_1699455.jpg, predicted: Boeing 747
1/1 [==============================] - 0s 48ms/step
False: Image: a340_2130085.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 51ms/step
True: Image: a340_2193516.jpg, predicted: Airbus A 340
1/1 [==============================] - 0s 50ms/step
False: Image: a340_1794927.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 48ms/step
True: Image: a340_2084805.jpg, predicted: Airbus A 340
1/1 [==============================] - 0s 54ms/step
False: Image: a340_2215390.jpg, predicted: Boeing 767
1/1 [==============================] - 0s 50ms/step
False: Image: a340_2177828.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 50ms/step
False: Image: a340_1718898.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 46ms/step
True: Image: a340_1634020.jpg, predicted: Airbus A 340
1/1 [==============================] - 0s 49ms/step
True: Image: a340_1732551.jpg, predicted: Airbus A 340
************
True predictions: 4 / 10
In [ ]:
predictions_test_set(b_737_test_dir, 3)
1/1 [==============================] - 0s 48ms/step
True: Image: b737_1237623.jpg, predicted: Boeing 737
1/1 [==============================] - 0s 46ms/step
True: Image: b737_1163792.jpg, predicted: Boeing 737
1/1 [==============================] - 0s 52ms/step
True: Image: b737_0851302.jpg, predicted: Boeing 737
1/1 [==============================] - 0s 50ms/step
True: Image: b737_0558343.jpg, predicted: Boeing 737
1/1 [==============================] - 0s 51ms/step
True: Image: b737_1115400.jpg, predicted: Boeing 737
1/1 [==============================] - 0s 51ms/step
True: Image: b737_0237107.jpg, predicted: Boeing 737
1/1 [==============================] - 0s 51ms/step
False: Image: b737_0218042.jpg, predicted: Boeing 747
1/1 [==============================] - 0s 52ms/step
True: Image: b737_0973082.jpg, predicted: Boeing 737
1/1 [==============================] - 0s 48ms/step
False: Image: b737_0482804.jpg, predicted: Embraer E jet
1/1 [==============================] - 0s 50ms/step
True: Image: b737_0523207.jpg, predicted: Boeing 737
************
True predictions: 8 / 10
In [ ]:
predictions_test_set(b_747_test_dir, 4)
1/1 [==============================] - 0s 50ms/step
False: Image: b747_1836113.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 51ms/step
False: Image: b747_2162684.jpg, predicted: Airbus A 320
1/1 [==============================] - 0s 51ms/step
True: Image: b747_1896036.jpg, predicted: Boeing 747
1/1 [==============================] - 0s 52ms/step
False: Image: b747_1707900.jpg, predicted: Boeing 767
1/1 [==============================] - 0s 52ms/step
False: Image: b747_2226313.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 51ms/step
False: Image: b747_2167150.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 50ms/step
True: Image: b747_1864991.jpg, predicted: Boeing 747
1/1 [==============================] - 0s 50ms/step
False: Image: b747_1739526.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 51ms/step
True: Image: b747_2217964.jpg, predicted: Boeing 747
1/1 [==============================] - 0s 52ms/step
False: Image: b747_1821602.jpg, predicted: Boeing 767
************
True predictions: 3 / 10
In [ ]:
predictions_test_set(b_767_test_dir, 5)
1/1 [==============================] - 0s 49ms/step
True: Image: b767_0981955.jpg, predicted: Boeing 767
1/1 [==============================] - 0s 49ms/step
True: Image: b767_0905077.jpg, predicted: Boeing 767
1/1 [==============================] - 0s 54ms/step
True: Image: b767_0940358.jpg, predicted: Boeing 767
1/1 [==============================] - 0s 58ms/step
True: Image: b767_0097107.jpg, predicted: Boeing 767
1/1 [==============================] - 0s 47ms/step
True: Image: b767_0087248.jpg, predicted: Boeing 767
1/1 [==============================] - 0s 49ms/step
True: Image: b767_2054456.jpg, predicted: Boeing 767
1/1 [==============================] - 0s 51ms/step
True: Image: b767_2021838.jpg, predicted: Boeing 767
1/1 [==============================] - 0s 50ms/step
True: Image: b767_0867007.jpg, predicted: Boeing 767
1/1 [==============================] - 0s 53ms/step
True: Image: b767_2259027.jpg, predicted: Boeing 767
1/1 [==============================] - 0s 47ms/step
True: Image: b767_2209439.jpg, predicted: Boeing 767
************
True predictions: 10 / 10
In [ ]:
predictions_test_set(embraer_ejet_test_dir, 6)
1/1 [==============================] - 0s 51ms/step
False: Image: ejet_1912564.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 47ms/step
False: Image: ejet_2156795.jpg, predicted: Boeing 747
1/1 [==============================] - 0s 51ms/step
False: Image: ejet_1917223.jpg, predicted: Airbus A 380
1/1 [==============================] - 0s 58ms/step
False: Image: ejet_2259453.jpg, predicted: Boeing 737
1/1 [==============================] - 0s 48ms/step
True: Image: ejet_2094472.jpg, predicted: Embraer E jet
1/1 [==============================] - 0s 48ms/step
True: Image: ejet_1398982.jpg, predicted: Embraer E jet
1/1 [==============================] - 0s 49ms/step
True: Image: ejet_1367016.jpg, predicted: Embraer E jet
1/1 [==============================] - 0s 47ms/step
True: Image: ejet_1687909.jpg, predicted: Embraer E jet
1/1 [==============================] - 0s 49ms/step
True: Image: ejet_1796759.jpg, predicted: Embraer E jet
1/1 [==============================] - 0s 49ms/step
True: Image: ejet_1679201.jpg, predicted: Embraer E jet
************
True predictions: 6 / 10

Visualizing the results¶

In [ ]:
path = '/Users/aliou/Downloads/huge_projects/aircraft_reco/tests'
In [ ]:
def plot_test_set_results(path,  n_ligne = 3, n_col = 4):

  """
    Cette fonction prends en paramètre un chemin d'accès, le nombre de lignes et de colonnes (par défaut 3,4) pour éffectuer des prédictions
    sur toutes les images contenu dans le repertoire, éffectuer les prédictions et afficher en comparant les résultats.

    Paramètres:
      - path: Chemin où sont contenu les images. (Une boucle créera une la source complète image par image afin de la prédire)
      - n_ligne: Nombre de ligne.
      - n_col: Nombre de colonnes. 
      (Les lignes et colonnes finales seront calculées)
  """  
    
  plt.figure(figsize = ((n_ligne * n_col), (max(n_ligne, n_col) * 2) ))

  files = os.listdir(path)

  for i, filename in enumerate(files):
      
    if filename != '.DS_Store':
        
        ax = plt.subplot(n_ligne, n_col, i + 1)

        x = rgb_to_grayscale(path + '/' + filename)
        preds_probs = model.predict(np.expand_dims(x, axis = 0))
        predicted = np.argmax(preds_probs, axis = 1)[0]

        img = image.load_img(path + '/' + filename,target_size = target_dim)
        plt.axis("off")
        plt.imshow(img)

        if predicted == 0 :
          ax.set_title(f"{filename[0:4]} model says : A 320'")
        elif predicted == 1:
          ax.set_title(f"{filename[0:4]} model says : A 340")
        elif predicted == 2:
          ax.set_title(f"{filename[0:4]} model says : A 380")
        elif predicted == 3:
          ax.set_title(f"{filename[0:4]} model says : B 737")
        elif predicted == 4:
          ax.set_title(f"{filename[0:4]} model says : B 747")
        elif predicted == 5:
          ax.set_title(f"{filename[0:4]} model says : B 767")
        elif predicted == 6:
          ax.set_title(f"{filename[0:4]} model says : E jet")
In [ ]:
plot_test_set_results(path)
1/1 [==============================] - 0s 52ms/step
1/1 [==============================] - 0s 61ms/step
1/1 [==============================] - 0s 51ms/step
1/1 [==============================] - 0s 49ms/step
1/1 [==============================] - 0s 47ms/step
1/1 [==============================] - 0s 54ms/step
1/1 [==============================] - 0s 60ms/step
1/1 [==============================] - 0s 76ms/step
1/1 [==============================] - 0s 114ms/step
1/1 [==============================] - 0s 46ms/step
1/1 [==============================] - 0s 48ms/step
1/1 [==============================] - 0s 49ms/step
1/1 [==============================] - 0s 50ms/step
1/1 [==============================] - 0s 48ms/step

Save model

In [ ]:
#model.save('aircraft_model_096.keras', overwrite = True)